// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CXFormsElement2.
//   $Id$

#include "stdafx.h"
#include "xformselement2.h"
#include <HTMLDOMTranslator.h>
#include <DOM2Core_i.c>

#include <Common/ResourceHandling/msw/loadHTMLResource.h>
#include "XFormsXFormsElement.h"
#include "SingleNodeBinding.h"

//UIEventFirer macro, calls the FireEvent function 
//	with the appropriate variables.
#define UIEventFirer(className,evName,evPosition) \
	HRESULT className::on##evName(){return FireEvent(m_alEventCookies[evPosition],0);}

#define EVENT_ARRAYPOSITION_FP_PUTVALUE 0
#define EVENT_ARRAYPOSITION_FP_PUTFOCUS 1
#define EVENT_ARRAYPOSITION_FP_UIREFRESH 2

const CComBSTR * const CXFormsElement2::sm_asEventNames[] 
= 
{
	&EVENT_FP_PUTVALUE,
	&EVENT_FP_PUTFOCUS,
	&EVENT_FP_UIREFRESH
};

static const CComBSTR cg_sTabIndex = L"tabindex";
static const CComVariant cg_vNegativeOne = L"-1";

HRESULT CXFormsElement2::onfp_putvalue(BSTR bsVal)
{
	HRESULT hr = E_NOINTERFACE;
	CComPtr<IMutationEvent> pMutation;
	pMutation.CoCreateInstance(CLSID_MutationEvent);
	if(pMutation)
	{
		CComVariant pVar0(0L);
		hr = pMutation->initMutationEvent(EVENT_FP_PUTVALUE,false,false,pVar0,0,bsVal,0,2);
		BOOL bCancelled;
		hr = dispatchEvent(pMutation,&bCancelled);
		m_sCurrentValue = bsVal;
	}
	return hr;
}

UIEventFirer(CXFormsElement2,fp_putfocus,EVENT_ARRAYPOSITION_FP_PUTFOCUS)
UIEventFirer(CXFormsElement2,fp_uirefresh,EVENT_ARRAYPOSITION_FP_UIREFRESH)

CXFormsElement2::CXFormsElement2(void)
{
}

CXFormsElement2::~CXFormsElement2(void)
{
}

HRESULT CXFormsElement2::ElementReady()
{
	HRESULT hr = __super::ElementReady();

	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	if(pEl)
	{
		pEl->setAttribute(cg_sTabIndex, cg_vNegativeOne, 0);

		if(m_pImpl && m_pImpl->m_sAppearance)
		{
			CComBSTR sClassName;
			pEl->get_className(&sClassName);
			sClassName += L" appearance-";
			sClassName +=  m_pImpl->m_sAppearance;
			pEl->put_className(sClassName);
		}
	}

	return hr;
}

HRESULT CXFormsElement2::render()
{
	return S_OK;
}


 HRESULT CXFormsElement2::get_value(BSTR* psValue)
{
	HRESULT hr = E_NOTIMPL;


	return hr;
}

 HRESULT CXFormsElement2::get_valuechanging(BSTR* psValue)
{
	HRESULT hr = E_NOTIMPL;

	return hr;
}


HRESULT CXFormsElement2::put_value(BSTR bsValue)
{
	HRESULT hr = E_NOTIMPL;

	return hr;
}

HRESULT CXFormsElement2::put_navindex(BSTR bsValue)
{
	HRESULT hr = E_NOTIMPL;

	return hr;
}

HRESULT CXFormsElement2::put_accesskey(BSTR bsValue)
{
	HRESULT hr = E_NOTIMPL;

	return hr;
}


/// <summary>
/// <c>attachEvents()</c> attaches a list of events for the current object and the specifed HTML Element.
/// <param name="pEventSink">Specfies the event sink which will dispatch the events</param>
/// <param name="pEl2">The HTML element which the current object requires notifcation when it fires the specified events.</param>
/// <param name="pEventList">List of events to attach. The default value of this paramater is zero. If the value is zero then the current list held by the objects (<c> m_lstEventNames </c>) will be attached.</param>
/// </summary>
HRESULT CXFormsElement2::attachEvents(IHTMLElement2 * pEl2, CComBSTRPtrList * pEventList)
{
	HRESULT hr = S_OK;
	if(m_pEventSink)
	{
		if(pEl2)
		{
			VARIANT_BOOL b = true;
			// A list hasn't been passed to the function then we use 
			// the list held by this object (m_lstEventNames)
			if(!pEventList)
				pEventList = &m_lstEventNames;
			if(pEventList->size() > 0)
			{
				CComBSTRPtrList::iterator i = pEventList->begin();

				while(i != pEventList->end() && SUCCEEDED(hr) && b)
				{
					hr = pEl2->attachEvent((*i)->m_str,m_pEventSink,&b);
					//TODO: add error handling to allow us to know which
					//		event failed to attach;
					ASSERT(SUCCEEDED(hr) && b);
					++i;

				}
			}
			else
				ASSERT(0);
		}
	}
	
	return hr;
}


HRESULT CXFormsElement2::formControlInitialize()
{
	//TODO: this should probably be in a more appropriate class
	//	(derived from this one.)
	HRESULT hr = S_OK;
	CImplSingleNodeBinding * pNB = dynamic_cast<CImplSingleNodeBinding *>(m_pFormControl);
	if(pNB)
	{
		CComBSTR sType;
		pNB->get_type(&sType);
		setSchemaTypeForUI(sType);
	}

	return hr;
}

const CComBSTR const sAttrNameSchemaType(L"schemaType");

HRESULT CXFormsElement2::setSchemaTypeForUI(CComBSTR & sType)
{
	HRESULT hr;
	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	if(pEl)
	{
		hr = pEl->setAttribute(sAttrNameSchemaType,CComVariant(sType));
	}
	else
	{
		hr = E_FAIL;
	}
	return hr;
}

HRESULT CXFormsElement2::resetSchemaTypeForUI(CComBSTR & sType)
{
	HRESULT hr;
	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	if(pEl)
	{
		CComVariant vtOldType;
		pEl->getAttribute(sAttrNameSchemaType,2,&vtOldType);
		if(vtOldType.vt == VT_BSTR && sType == vtOldType.bstrVal) 
		{
			hr = S_FALSE;
		}
		else
		{
			hr = pEl->setAttribute(sAttrNameSchemaType,CComVariant(sType));
		}
		
	}
	else
	{
		hr = E_FAIL;
	}
	return hr;
}

HRESULT LoadWebBrowserFromStream(IDispatch* pHtmlDoc, IStream* pStream)
{
	HRESULT hr = S_OK;
	IPersistStreamInit* pPersistStreamInit = NULL;

    // Query for IPersistStreamInit.
    hr = pHtmlDoc->QueryInterface( IID_IPersistStreamInit,  (void**)&pPersistStreamInit );
    if ( SUCCEEDED(hr) )
    {
        // Initialize the document.
        hr = pPersistStreamInit->InitNew();
        if ( SUCCEEDED(hr) )
        {
            // Load the contents of the stream.
            hr = pPersistStreamInit->Load( pStream );
        }
        pPersistStreamInit->Release();
    }
	return hr;
}

HRESULT CXFormsElement2::focus()
{
	HRESULT hr = E_NOTIMPL;
	return hr;
}

HRESULT CXFormsElement2::Detach()
{
	HRESULT hr;
	hr = __super::Detach();
	return hr;
}

HRESULT CXFormsElement2::put_style(BSTR /*sWhere*/, BSTR /*sStyle*/)
{
	return E_NOTIMPL;
}

HRESULT CXFormsElement2::UIExecScript(BSTR /*bsScript*/)
{
	return E_NOTIMPL;
}

HRESULT CXFormsElement2::UIExecFunction(BSTR /*bsFunctionName*/,DISPPARAMS * /*pParams*/,VARIANT * /*pvtResult*/,DISPID & /*dispid*/)
{
	return E_FAIL;
}



HRESULT CXFormsElement2::getXML(IXMLDOMNode ** ppXML)
{
	HRESULT hr = E_NOINTERFACE;
	CComQIPtr<IHTMLDOMNode> pMaster = GetControllingUnknown();
	if(pMaster)
	{
		CHTMLDOMTranslator translator(CLSID_DOMDocument60);
		hr = translator.cloneNode(pMaster,ppXML,true);
	}
	return hr;

}


HRESULT CXFormsElement2::SetClassProperty(bool bEnabled, bool bReadOnly, bool bValid, bool bRequired) 
{
	HRESULT hr = S_FALSE;

	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	if(pEl) {
		CComBSTR sClassNames;
		pEl->get_className(&sClassNames);
		CStringW sNewClassNames(sClassNames);
		bool bModified = false;
		bool bTempModified = false;
		// Set Valid or Invalid css class 
		if(bValid && !(bTempModified = sNewClassNames.Replace(cssClassInvalid, cssClassValid) > 0) && sNewClassNames.Find(cssClassValid) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassValid;
		} else if(!bValid && !(bTempModified = sNewClassNames.Replace(cssClassValid, cssClassInvalid) > 0) && sNewClassNames.Find(cssClassInvalid) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassInvalid;
		}
		if(bTempModified)
			bModified = true;
		// Set ReadOnly or ReadWrite css class
		if(bReadOnly && !(bTempModified = sNewClassNames.Replace(cssClassReadWrite, cssClassReadOnly) > 0) && sNewClassNames.Find(cssClassReadOnly) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassReadOnly;
		} else if( !bReadOnly && !(bTempModified = sNewClassNames.Replace(cssClassReadOnly, cssClassReadWrite) > 0) && sNewClassNames.Find(cssClassReadWrite) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassReadWrite;
		}
		if(bTempModified)
			bModified = true;
		// Set Required or Optional css class
		if(bRequired && !(bTempModified = sNewClassNames.Replace(cssClassOptional, cssClassRequired) > 0) && sNewClassNames.Find(cssClassRequired) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassRequired;
		} else if(!bRequired && !(bTempModified = sNewClassNames.Replace(cssClassRequired, cssClassOptional) > 0) && sNewClassNames.Find(cssClassOptional) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassOptional;
		}
		if(bTempModified)
			bModified = true;
		// Set Endabled or Disabled css class
		if(bEnabled && !(bTempModified = sNewClassNames.Replace(cssClassDisabled, cssClassEnabled) > 0)&& sNewClassNames.Find(cssClassEnabled) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassEnabled;
		} else if(!bEnabled && !(bTempModified = sNewClassNames.Replace(cssClassEnabled, cssClassDisabled) > 0) && sNewClassNames.Find(cssClassDisabled) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassDisabled;
		}
		if(bTempModified && !bModified)
			bModified = true;

		if(bModified) {
			sClassNames.Attach(sNewClassNames.AllocSysString());
			hr = pEl->put_className(sClassNames);
		}
	}

	return hr;
}




HRESULT CXFormsElement2::InitBehaviour()
{
	__super::InitBehaviour();
	CComPtr<IHTMLElement> pMaster;
	GetMasterElement(&pMaster);
	if(pMaster)
		pMaster->setAttribute(L"liveChildren",CComVariant(L"yes"),1);
	return RegisterCustomEvents(sm_asEventNames,CXFORMSELEMENT2_EVENTS_LENGTH, m_alEventCookies);
}

